import { App, MarkdownView, EventRef, ItemView, WorkspaceLeaf, Menu, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
import { Transformer } from 'markmap-lib';
import { Markmap } from 'markmap-view';
import { createSVG } from './markmap-svg';
import ObsidianMarkmap from './markmap-link';
import { Toolbar } from 'markmap-toolbar';

// Remember to rename these classes and interfaces!
const MY_VIEW_TYPE = 'm-mind-map';

interface MyPluginSettings {
	mySetting: string;
}

const DEFAULT_SETTINGS: MyPluginSettings = {
	mySetting: 'default'
}

export default class MyPlugin extends Plugin {
	settings: MyPluginSettings;
    mindmapView: MindmapView;
    myview:WorkspaceLeaf;

	async onload() {
		await this.loadSettings();

		const ribbonIconEl = this.addRibbonIcon('dice', 'open mindmap', (evt: MouseEvent) => {
            this.initPreview();
		});

        this.registerView(
            MY_VIEW_TYPE,
            (leaf: WorkspaceLeaf) =>
              (this.mindmapView = new MindmapView(leaf,"mindmap"))
          );

		ribbonIconEl.addClass('my-plugin-ribbon-class');

		//this.addCommand({
		//	id: 'open-sample-modal-simple',
		//	name: 'Open sample modal (simple)',
		//	callback: () => {
		//		new SampleModal(this.app).open();
		//	}
		//});
        
		//this.addCommand({
		//	id: 'sample-editor-command',
		//	name: 'Sample editor command',
		//	editorCallback: (editor: Editor, view: MarkdownView) => {
		//		console.log(editor.getSelection());
		//		editor.replaceSelection('Sample Editor Command');
		//	}
		//});
        
		this.addCommand({
			id: 'open-mindmap',
			name: 'Open mindmap',
			checkCallback: (checking: boolean) => {
				const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
				if (markdownView) {
					// If checking is true, we're simply "checking" if the command can be run.
					// If checking is false, then we want to actually perform the operation.
					if (!checking) {
						this.initPreview();
					}

					// This command will only show up in Command Palette when the check function returns true
					return true;
				}
			}
		});

		// This adds a settings tab so the user can configure various aspects of the plugin
		this.addSettingTab(new SampleSettingTab(this.app, this));

	}

	onunload() {

	}

    fetchData = () => {
        const view = this.app.workspace.getActiveViewOfType(MarkdownView)
    
        if (view instanceof MarkdownView) {
          const name = view.getDisplayText()
          const content = view.getViewData()
    
          return({ name, content })
        } else {
          const leaves = this.app.workspace.getLeavesOfType("markdown")
    
          if (leaves.length == 0) {
            return(null)
          }
        }
      }

    async initPreview() {
        const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
        const mindmapView = this.app.workspace.getActiveViewOfType(MindmapView)?.leaf;
        if (this.app.workspace.getLeavesOfType(MY_VIEW_TYPE).length > 0) {
            mindmapView && this.app.workspace.setActiveLeaf(mindmapView,{focus:true})
            return;
        }
        if (markdownView) {
            this.myview = this.app.workspace.getLeaf('split','horizontal');
            const mmPreview = new MindmapView(this.myview, 'Mindmap');
            this.myview.open(mmPreview);
            return;
        }
        new Notice('Only markdown file allow!');
      }

	async loadSettings() {
		this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
	}

	async saveSettings() {
		await this.saveData(this.settings);
	}
}

//class SampleModal extends Modal {
//	constructor(app: App) {
//		super(app);
//	}

//	onOpen() {
//		const {contentEl} = this;
//		contentEl.setText('Woah!');
//	}

//	onClose() {
//		const {contentEl} = this;
//		contentEl.empty();
//	}
//}


class MindmapView extends ItemView {
    filePath: string;
    fileName: string;
    mtitle: string;
    vdata: string;
    svg: SVGElement;
    obsmarkmap:ObsidianMarkmap;
    linkedLeaf: WorkspaceLeaf;
    listeners: EventRef[];

    constructor(leaf: WorkspaceLeaf, mtitle: string){
        super(leaf);
        this.linkedLeaf = leaf;
        this.mtitle = mtitle;
    }

    getViewType(): string {
        return MY_VIEW_TYPE;
    }

    getDisplayText(): string {
        return this.mtitle ?? 'MindMap';
    }

    getIcon() {
        return "dot-network";
    }

    async onOpen() {
        this.obsmarkmap = new ObsidianMarkmap(this.app.vault);
        this.registerActiveLeafUpdate();
        this.listeners = [
            this.app.workspace.on('layout-change', () => this.update()),
            this.app.workspace.on('resize', () => this.update()),
            this.app.workspace.on('css-change', () => this.update()),
            this.leaf.on('group-change', (group) => this.updateLinkedLeaf(group, this))
        ];
    }

    async onClose() {
        this.listeners.forEach(listener => this.app.workspace.offref(listener));
    }

    updateLinkedLeaf(group: string, mmView: MindmapView) {
        console.log(group);
        if(group === null) {
            //mmView.linkedLeaf = null;
            return;
        }
        const mdLinkedLeaf = this.app.workspace.getGroupLeaves(group).filter(l => l.view.getViewType() === MY_VIEW_TYPE)[0];
        mmView.linkedLeaf = mdLinkedLeaf;
        this.checkAndUpdate();
    }

    registerActiveLeafUpdate() {
        this.registerInterval(
            window.setInterval(() => this.checkAndUpdate(), 2000)
        );
    }
    async checkAndUpdate() {
        try {
            if(await this.checkActiveLeaf()) {
                this.update();
            }
        } catch (error) {
            console.error(error)
        }
    }

    async checkActiveLeaf() {
        // console.log(this.app.workspace.activeLeaf?.getDisplayText());
        if(this.app.workspace.getActiveViewOfType(MindmapView)?.getViewType() === MY_VIEW_TYPE){
            return false;
        }
        return true;
    }

    async update(){
        const data = this.fetchData();
        if(data && data.content!=this.vdata) {
            this.vdata = data.content;
            const transformer = new Transformer();
            const { root } = transformer.transform(data.content);

            const root2 = this.obsmarkmap.updateInternalLinks(root);

            this.svg = createSVG(this.containerEl, '1em');
            const mm = Markmap.create(this.svg);
            const { el } = Toolbar.create(mm);
            
            const bar = document.createElement('div');
            bar.id = 'marktoolbar';
            bar.setAttr('style', 'position: absolute; bottom: 8em; right:2em;');
            bar.appendChild(el);
            try {
                const existing = document.getElementById('marktoolbar');
                if(existing) {
                    existing.parentElement?.removeChild(existing);
                }
            } catch (error) {
                console.log(error)
            }
            const container = this.containerEl.children[1];
            container.append(bar);
            
            mm.setData(root2);
            mm.fit()
            this.mtitle = data?.name ? `MindMap of ${data.name}` : 'MindMap'; 
            this.load();
            window.setTimeout(() => this.load(), 2000)
        }
    }

    fetchData = () => {
        const view = this.app.workspace.getActiveViewOfType(MarkdownView)
    
        if (view instanceof MarkdownView) {
          const name = view.getDisplayText()
          const content = view.getViewData()
    
          return({ name, content })
        } else {
            return(null)
        }
    }

    onMoreOptionsMenu(menu: Menu) {    
        menu
        .addItem((item) => 
            item
            .setIcon('pin')
            .setTitle('Pin')
            .onClick(() => alert('pin'))
        )
        .addSeparator()
        .addItem((item) => 
            item
            .setIcon('image-file')
            .setTitle('Copy screenshot')
            .onClick(() => alert('copy'))  
        ); 
        menu.showAtPosition({x: 0, y: 0});
    }
}

class SampleSettingTab extends PluginSettingTab {
	plugin: MyPlugin;

	constructor(app: App, plugin: MyPlugin) {
		super(app, plugin);
		this.plugin = plugin;
	}

	display(): void {
		const {containerEl} = this;

		containerEl.empty();

		new Setting(containerEl)
			.setName('Setting #1')
			.setDesc('It\'s a secret')
			.addText(text => text
				.setPlaceholder('Enter your secret')
				.setValue(this.plugin.settings.mySetting)
				.onChange(async (value) => {
					this.plugin.settings.mySetting = value;
					await this.plugin.saveSettings();
				}));
	}
}
